﻿#include "MapObject.h"
#include "../../Shared/ServerPackets.h"
#include "../../Shared/Functions/Functions.h"
#include "../Settings.h"
#include "../../Shared/Globals.h"
#include "NPCObject.h"
#include "Monsters/TrapRock.h"

using namespace Server::MirDatabase;
using namespace Server::MirEnvir;
using namespace Server::MirObjects::Monsters;
namespace S = ServerPackets;

namespace Server::MirObjects
{

    Server::MessageQueue *MapObject::getMessageQueue()
    {
        return MessageQueue::getInstance();
    }

    Server::MirEnvir::Envir *MapObject::getEnvir()
    {
        return Envir::getMain();
    }

    void MapObject::setCurrentMap(Map *value)
    {
        _currentMap = value;
        setCurrentMapIndex(_currentMap != nullptr ? _currentMap->Info->Index : 0);
    }

    Map *MapObject::getCurrentMap() const
    {
        return _currentMap;
    }

    unsigned char MapObject::getPercentHealth() const
    {
        return static_cast<unsigned char>(getHealth() / static_cast<float>(getMaxHealth()) * 100);
    }

    int MapObject::getPKPoints() const
    {
        return PKPoints;
    }

    void MapObject::setPKPoints(int value)
    {
        PKPoints = value;
    }

    bool MapObject::getHidden() const
    {
        return _hidden;
    }

    void MapObject::setHidden(bool value)
    {
        if (_hidden == value)
        {
            return;
        }
        _hidden = value;
        S::ObjectHidden *tempVar = new S::ObjectHidden(), *CurrentLocation;
        tempVar->ObjectID = ObjectID;
        tempVar->Hidden = value;
        getCurrentMap()->Broadcast(tempVar, CurrentLocation);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
    }

    bool MapObject::getObserver() const
    {
        return _observer;
    }

    void MapObject::setObserver(bool value)
    {
        if (_observer == value)
        {
            return;
        }
        _observer = value;
        if (!_observer)
        {
            BroadcastInfo();
        }
        else
        {
            S::ObjectRemove *tempVar = new S::ObjectRemove();
            tempVar->ObjectID = ObjectID;
            Broadcast(tempVar);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
        }
    }

    bool MapObject::getSneakingActive() const
    {
        return _sneakingActive;
    }

    void MapObject::setSneakingActive(bool value)
    {
        if (_sneakingActive == value)
        {
            return;
        }
        _sneakingActive = value;

        setObserver(_sneakingActive);

        //CurrentMap.Broadcast(new S.ObjectSneaking { ObjectID = ObjectID, SneakingActive = value }, CurrentLocation);
    }

    bool MapObject::getSneaking() const
    {
        return _sneaking;
    }

    void MapObject::setSneaking(bool value)
    {
        _sneaking = value;
        setSneakingActive(value);
    }

    MapObject *MapObject::getTarget() const
    {
        return _target;
    }

    void MapObject::setTarget(MapObject *value)
    {
        if (_target == value)
        {
            return;
        }
        _target = value;
    }

    AttackMode MapObject::getAMode() const
    {
        return AMode;
    }

    void MapObject::setAMode(AttackMode value)
    {
        AMode = value;
    }

    PetMode MapObject::getPMode() const
    {
        return PMode;
    }

    void MapObject::setPMode(PetMode value)
    {
        PMode = value;
    }

    bool MapObject::getBlocking() const
    {
        return true;
    }

    Point *MapObject::getFront() const
    {
        return Functions::PointMove(getCurrentLocation(), getDirection(), 1);
    }

    Point *MapObject::getBack() const
    {
        return Functions::PointMove(getCurrentLocation(), getDirection(), -1);
    }

    void MapObject::Process()
    {
        if (Master != nullptr && Master->Node == nullptr)
        {
            Master = nullptr;
        }
        if (LastHitter != nullptr && LastHitter->Node == nullptr)
        {
            LastHitter = nullptr;
        }
        if (EXPOwner != nullptr && EXPOwner->Node == nullptr)
        {
            EXPOwner = nullptr;
        }
        if (getTarget() != nullptr && (getTarget()->Node == nullptr || getTarget()->Dead))
        {
            setTarget(nullptr);
        }
        if (Owner != nullptr && Owner->Node == nullptr)
        {
            Owner = nullptr;
        }

        if (getPKPoints() > 0 && getEnvir()->getTime() > PKPointTime)
        {
            PKPointTime = getEnvir()->getTime() + Settings::PKDelay * Settings::Second;
            setPKPoints(getPKPoints() - 1);
        }

        if (LastHitter != nullptr && getEnvir()->getTime() > LastHitTime)
        {
            LastHitter = nullptr;
        }

        if (EXPOwner != nullptr && getEnvir()->getTime() > EXPOwnerTime)
        {
            EXPOwner = nullptr;
        }

        for (int i = 0; i < ActionList.size(); i++)
        {
            if (getEnvir()->getTime() < ActionList[i]->Time)
            {
                continue;
            }
            Process(ActionList[i]);
            ActionList.erase(ActionList.begin() + i);
        }
    }

    int MapObject::GetAttackPower(int min, int max)
    {
        if (min < 0)
        {
            min = 0;
        }
        if (min > max)
        {
            max = min;
        }

        if (Stats[Stat::Luck] > 0)
        {
            if (Stats[Stat::Luck] > getEnvir()->Random->Next(Settings::MaxLuck))
            {
                return max;
            }
        }
        else if (Stats[Stat::Luck] < 0)
        {
            if (Stats[Stat::Luck] < -getEnvir()->Random->Next(Settings::MaxLuck))
            {
                return min;
            }
        }

        return getEnvir()->Random->Next(min, max + 1);
    }

    int MapObject::GetRangeAttackPower(int min, int max, int range)
    {
        //maxRange = highest possible damage
        //minRange = lowest possible damage

        Decimal x = (static_cast<Decimal>(min) / (Globals::MaxAttackRange)) * (Globals::MaxAttackRange - range);

        min -= static_cast<int>(std::floor(x));

        return GetAttackPower(min, max);
    }

    int MapObject::GetDefencePower(int min, int max)
    {
        if (min < 0)
        {
            min = 0;
        }
        if (min > max)
        {
            max = min;
        }

        return getEnvir()->Random->Next(min, max + 1);
    }

    void MapObject::Remove(PlayerObject *player)
    {
        S::ObjectRemove *tempVar = new S::ObjectRemove();
        tempVar->ObjectID = ObjectID;
        player->Enqueue(tempVar);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
    }

    void MapObject::Add(PlayerObject *player)
    {
        if (getRace() == ObjectType::Merchant)
        {
            NPCObject *NPC = static_cast<NPCObject*>(this);
            NPC->CheckVisible(player, true);
            return;
        }

        player->Enqueue(GetInfo());

        //if (Race == ObjectType.Player)
        //{
        //    PlayerObject me = (PlayerObject)this;
        //    player.Enqueue(me.GetInfoEx(player));
        //}
        //else
        //{
        //    player.Enqueue(GetInfo());
        //}
    }

    void MapObject::Remove(MonsterObject *monster)
    {

    }

    void MapObject::Add(MonsterObject *monster)
    {

    }

    bool MapObject::CanFly(Point *target)
    {
        Point *location = getCurrentLocation();

        while (location != target)
        {
            MirDirection dir = Functions::DirectionFromPoint(location, target);

            location = Functions::PointMove(location, dir, 1);

            if (location->X < 0 || location->Y < 0 || location->X >= getCurrentMap()->Width || location->Y >= getCurrentMap()->Height)
            {
                return false;
            }

            if (!getCurrentMap()->GetCell(location)->getValid())
            {
                return false;
            }
        }

        return true;
    }

    void MapObject::Spawned()
    {
        Node = getEnvir()->Objects.push_back(this);
        if ((getRace() == ObjectType::Monster) && Settings::Multithreaded)
        {
            SpawnThread = getCurrentMap()->Thread;
            NodeThreaded = getEnvir()->MobThreads[SpawnThread]->ObjectsList.push_back(this);
        }

        OperateTime = getEnvir()->getTime() + getEnvir()->Random->Next(OperateDelay);

        InSafeZone = getCurrentMap() != nullptr && getCurrentMap()->GetSafeZone(getCurrentLocation()) != nullptr;
        BroadcastInfo();
        BroadcastHealthChange();
    }

    void MapObject::Despawn()
    {
        S::ObjectRemove *tempVar = new S::ObjectRemove();
        tempVar->ObjectID = ObjectID;
        Broadcast(tempVar);
        getEnvir()->Objects.remove(Node);
        if (Settings::Multithreaded && (getRace() == ObjectType::Monster))
        {
            getEnvir()->MobThreads[SpawnThread]->ObjectsList->remove(NodeThreaded);
        }

        ActionList.clear();

        for (int i = Pets.size() - 1; i >= 0; i--)
        {
            Pets[i]->Die();
        }

        Node = nullptr;

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
    }

    MapObject *MapObject::FindObject(unsigned int targetID, int dist)
    {
        for (int d = 0; d <= dist; d++)
        {
            for (int y = getCurrentLocation()->Y - d; y <= getCurrentLocation()->Y + d; y++)
            {
                if (y < 0)
                {
                    continue;
                }
                if (y >= getCurrentMap()->Height)
                {
                    break;
                }

                for (int x = getCurrentLocation()->X - d; x <= getCurrentLocation()->X + d; x += std::abs(y - getCurrentLocation()->Y) == d ? 1 : d * 2)
                {
                    if (x < 0)
                    {
                        continue;
                    }
                    if (x >= getCurrentMap()->Width)
                    {
                        break;
                    }

                    Cell *cell = getCurrentMap()->GetCell(x, y);
                    if (!cell->getValid() || cell->Objects.empty())
                    {
                        continue;
                    }

                    for (int i = 0; i < cell->Objects.size(); i++)
                    {
                        MapObject *ob = cell->Objects[i];
                        if (ob->ObjectID != targetID)
                        {
                            continue;
                        }

                        return ob;
                    }
                }
            }
        }
        return nullptr;
    }

    void MapObject::Broadcast(Packet *p)
    {
        if (p == nullptr || getCurrentMap() == nullptr)
        {
            return;
        }

        for (int i = getCurrentMap()->Players.size() - 1; i >= 0; i--)
        {
            PlayerObject *player = getCurrentMap()->Players[i];
            if (player == this)
            {
                continue;
            }

            if (Functions::InRange(getCurrentLocation(), player->getCurrentLocation(), Globals::DataRange))
            {
                player->Enqueue(p);
            }
        }
    }

    void MapObject::BroadcastInfo()
    {
        Broadcast(GetInfo());
        return;
    }

    int MapObject::GetArmour(DefenceType type, MapObject *attacker, bool &hit)
    {
        auto armour = 0;
        hit = true;
        switch (type)
        {
            case DefenceType::ACAgility:
                if (getEnvir()->Random->Next(Stats[Stat::Agility] + 1) > attacker->Stats[Stat::Accuracy])
                {
                    BroadcastDamageIndicator(DamageType::Miss);
                    hit = false;
                }
                armour = GetDefencePower(Stats[Stat::MinAC], Stats[Stat::MaxAC]);
                break;
            case DefenceType::AC:
                armour = GetDefencePower(Stats[Stat::MinAC], Stats[Stat::MaxAC]);
                break;
            case DefenceType::MACAgility:
                if (getEnvir()->Random->Next(Settings::MagicResistWeight) < Stats[Stat::MagicResist])
                {
                    BroadcastDamageIndicator(DamageType::Miss);
                    hit = false;
                }
                if (getEnvir()->Random->Next(Stats[Stat::Agility] + 1) > attacker->Stats[Stat::Accuracy])
                {
                    BroadcastDamageIndicator(DamageType::Miss);
                    hit = false;
                }
                armour = GetDefencePower(Stats[Stat::MinMAC], Stats[Stat::MaxMAC]);
                break;
            case DefenceType::MAC:
                if (getEnvir()->Random->Next(Settings::MagicResistWeight) < Stats[Stat::MagicResist])
                {
                    BroadcastDamageIndicator(DamageType::Miss);
                    hit = false;
                }
                armour = GetDefencePower(Stats[Stat::MinMAC], Stats[Stat::MaxMAC]);
                break;
            case DefenceType::Agility:
                if (getEnvir()->Random->Next(Stats[Stat::Agility] + 1) > attacker->Stats[Stat::Accuracy])
                {
                    BroadcastDamageIndicator(DamageType::Miss);
                    hit = false;
                }
                break;
        }
        return armour;
    }

    void MapObject::ApplyNegativeEffects(PlayerObject *attacker, DefenceType type, unsigned short levelOffset)
    {
        if (attacker->SpecialMode::HasFlag(SpecialItemMode::Paralize) && type != DefenceType::MAC && type != DefenceType::MACAgility && 1 == getEnvir()->Random->Next(1, 15))
        {
            Poison *tempVar = new Poison(), *attacker;
            tempVar->PType = PoisonType::Paralysis;
            tempVar->Duration = 5;
            tempVar->TickSpeed = 1000;
            ApplyPoison(tempVar, attacker);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
        }
        if ((attacker->Stats[Stat::Freezing] > 0) && (Settings::PvpCanFreeze) && type != DefenceType::MAC && type != DefenceType::MACAgility)
        {
            if ((getEnvir()->Random->Next(Settings::FreezingAttackWeight) < attacker->Stats[Stat::Freezing]) && (getEnvir()->Random->Next(levelOffset) == 0))
            {
                Poison *tempVar2 = new Poison(), *attacker;
                tempVar2->PType = PoisonType::Slow;
                tempVar2->Duration = std::min(10, (3 + getEnvir()->Random->Next(attacker->Stats[Stat::Freezing])));
                tempVar2->TickSpeed = 1000;
                ApplyPoison(tempVar2, attacker);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar2' statement was not added since tempVar2 was passed to a method or constructor. Handle memory management manually.
            }
        }
        if (attacker->Stats[Stat::PoisonAttack] > 0 && type != DefenceType::MAC && type != DefenceType::MACAgility)
        {
            if ((getEnvir()->Random->Next(Settings::PoisonAttackWeight) < attacker->Stats[Stat::PoisonAttack]) && (getEnvir()->Random->Next(levelOffset) == 0))
            {
                Poison *tempVar3 = new Poison(), *attacker;
                tempVar3->PType = PoisonType::Green;
                tempVar3->Duration = 5;
                tempVar3->TickSpeed = 1000;
                tempVar3->Value = std::min(10, 3 + getEnvir()->Random->Next(attacker->Stats[Stat::PoisonAttack]));
                ApplyPoison(tempVar3, attacker);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar3' statement was not added since tempVar3 was passed to a method or constructor. Handle memory management manually.
            }
        }
    }

    void MapObject::WinExp(unsigned int amount, unsigned int targetLevel)
    {


    }

    bool MapObject::CanGainGold(unsigned int gold)
    {
        return false;
    }

    void MapObject::WinGold(unsigned int gold)
    {

    }

    bool MapObject::Harvest(PlayerObject *player)
    {
        return false;
    }

    Buff *MapObject::AddBuff(BuffType type, MapObject *owner, int duration, Stats *stats, bool visible, bool infinite, bool stackable, bool refreshStats, std::vector<int> &values)
    {
        Buff buff;
        if (!HasBuff(type, buff))
        {
            buff = new Buff();
            buff->Type = type;
            buff->Caster = owner;
            buff->ObjectID = ObjectID;
            buff->ExpireTime = getEnvir()->getTime() + duration;

            Buffs.push_back(buff);
        }
        else if (stackable)
        {
            if (!buff->Stats->Equals(stats))
            {
                //TODO - Maybe get the best one instead?
                stats = buff->Stats;
            }

            buff->ExpireTime += duration;
        }
        else if (buff->ExpireTime < getEnvir()->getTime() + duration)
        {
            buff->ExpireTime = getEnvir()->getTime() + duration;
        }

        buff->Stackable = stackable;
        buff->Infinite = infinite;
        buff->Visible = visible;
        buff->Stats = (stats != nullptr) ? stats : new Stats();
        buff->Values = (values != nullptr) ? values : std::vector<int>(0);
        buff->Paused = false;

        switch (buff->Type)
        {
            case BuffType::MoonLight:
            case BuffType::DarkBody:
                setHidden(true);
                setSneaking(true);
                HideFromTargets();
                break;
            case BuffType::Hiding:
            case BuffType::ClearRing:
                setHidden(true);
                HideFromTargets();
                break;
        }

//C# TO C++ CONVERTER TODO TASK: A 'delete buff' statement was not added since buff was passed to a method or constructor. Handle memory management manually.
        return buff;
    }

    void MapObject::RemoveBuff(BuffType b)
    {
        for (int i = 0; i < Buffs.size(); i++)
        {
            if (Buffs[i]->Type != b)
            {
                continue;
            }

            Buffs[i]->Infinite = false;
            Buffs[i]->Paused = false;
            Buffs[i]->ExpireTime = getEnvir()->getTime();

            switch (b)
            {
                case BuffType::Hiding:
                case BuffType::MoonLight:
                case BuffType::DarkBody:
                    if (!HasAnyBuffs(b, {BuffType::ClearRing, BuffType::Hiding, BuffType::MoonLight, BuffType::DarkBody}))
                    {
                        setHidden(false);
                    }
                    break;
            }
        }
    }

    bool MapObject::HasBuff(BuffType type, Buff *&buff)
    {
        for (int i = 0; i < Buffs.size(); i++)
        {
            if (Buffs[i]->Type != type)
            {
                continue;
            }

            buff = Buffs[i];
            return true;
        }

        buff = nullptr;
        return false;
    }

    bool MapObject::HasAnyBuffs(BuffType exceptBuff, std::vector<BuffType> &types)
    {
        return Buffs.Select([&] (std::any x)
        {
            x::Type;
        }).Except(std::vector<BuffType> {exceptBuff}).Intersect(types)->Any();
    }

    void MapObject::HideFromTargets()
    {
        for (int y = getCurrentLocation()->Y - Globals::DataRange; y <= getCurrentLocation()->Y + Globals::DataRange; y++)
        {
            if (y < 0)
            {
                continue;
            }
            if (y >= getCurrentMap()->Height)
            {
                break;
            }

            for (int x = getCurrentLocation()->X - Globals::DataRange; x <= getCurrentLocation()->X + Globals::DataRange; x++)
            {
                if (x < 0)
                {
                    continue;
                }
                if (x >= getCurrentMap()->Width)
                {
                    break;
                }
                if (x < 0 || x >= getCurrentMap()->Width)
                {
                    continue;
                }

                Cell *cell = getCurrentMap()->GetCell(x, y);

                if (!cell->getValid() || cell->Objects.empty())
                {
                    continue;
                }

                for (int i = 0; i < cell->Objects.size(); i++)
                {
                    MapObject *ob = cell->Objects[i];
                    if (ob->getRace() != ObjectType::Monster)
                    {
                        continue;
                    }

                    if (ob->getTarget() == this && (!ob->CoolEye || ob->getLevel() < getLevel()))
                    {
                        ob->setTarget(nullptr);
                    }
                }
            }
        }
    }

    bool MapObject::CheckStacked()
    {
        Cell *cell = getCurrentMap()->GetCell(getCurrentLocation());

        if (!cell->Objects.empty())
        {
            for (int i = 0; i < cell->Objects.size(); i++)
            {
                MapObject *ob = cell->Objects[i];
                if (ob == this || !ob->getBlocking())
                {
                    continue;
                }
                return true;
            }
        }

        return false;
    }

    bool MapObject::Teleport(Map *temp, Point *location, bool effects, unsigned char effectnumber)
    {
        if (temp == nullptr || !temp->ValidPoint(location))
        {
            return false;
        }

        getCurrentMap()->RemoveObject(this);
        if (effects)
        {
            S::ObjectTeleportOut *tempVar = new S::ObjectTeleportOut();
            tempVar->ObjectID = ObjectID;
            tempVar->Type = effectnumber;
            Broadcast(tempVar);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
        }
        S::ObjectRemove *tempVar2 = new S::ObjectRemove();
        tempVar2->ObjectID = ObjectID;
        Broadcast(tempVar2);

        setCurrentMap(temp);
        setCurrentLocation(location);

        setInTrapRock(false);

        getCurrentMap()->AddObject(this);
        BroadcastInfo();

        if (effects)
        {
            S::ObjectTeleportIn *tempVar3 = new S::ObjectTeleportIn();
            tempVar3->ObjectID = ObjectID;
            tempVar3->Type = effectnumber;
            Broadcast(tempVar3);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar3' statement was not added since tempVar3 was passed to a method or constructor. Handle memory management manually.
        }

        BroadcastHealthChange();

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar2' statement was not added since tempVar2 was passed to a method or constructor. Handle memory management manually.
        return true;
    }

    bool MapObject::TeleportRandom(int attempts, int distance, Map *map)
    {
        if (map == nullptr)
        {
            map = getCurrentMap();
        }
        if (map->Cells.empty())
        {
            return false;
        }
        if (!map->WalkableCells.empty() && map->WalkableCells.empty())
        {
            return false;
        }

        if (map->WalkableCells.empty())
        {
            map->WalkableCells = std::vector<Point*>();

            for (int x = 0; x < map->Width; x++)
            {
                for (int y = 0; y < map->Height; y++)
                {
                    if (map->Cells[x][y].Attribute == CellAttribute::Walk)
                    {
                        Point tempVar(x, y);
                        map->WalkableCells.push_back(&tempVar);
                    }
                }
            }

            if (map->WalkableCells.empty())
            {
                return false;
            }
        }

        int cellIndex = getEnvir()->Random->Next(map->WalkableCells.size());

        return Teleport(map, map->WalkableCells[cellIndex]);
    }

    Point *MapObject::GetRandomPoint(int attempts, int distance, Map *map)
    {
        unsigned char edgeoffset = 0;

        if (map->Width < 150)
        {
            if (map->Height < 30)
            {
                edgeoffset = 2;
            }
            else
            {
                edgeoffset = 20;
            }
        }

        for (int i = 0; i < attempts; i++)
        {
            Point *location;

            if (distance <= 0)
            {
                location = new Point(edgeoffset + getEnvir()->Random->Next(map->Width - edgeoffset), edgeoffset + getEnvir()->Random->Next(map->Height - edgeoffset)); //Can adjust Random Range...
            }
            else
            {
                location = new Point(getCurrentLocation()->X + getEnvir()->Random->Next(-distance, distance + 1), getCurrentLocation()->Y + getEnvir()->Random->Next(-distance, distance + 1));
            }


            if (map->ValidPoint(location))
            {
//C# TO C++ CONVERTER TODO TASK: A 'delete location' statement was not added since location was passed to a method or constructor. Handle memory management manually.
                return location;
            }

//C# TO C++ CONVERTER TODO TASK: A 'delete location' statement was not added since location was passed to a method or constructor. Handle memory management manually.
        }

        return new Point(0, 0);
    }

    void MapObject::BroadcastHealthChange()
    {
        if (getRace() != ObjectType::Player && getRace() != ObjectType::Monster)
        {
            return;
        }

        unsigned char time = std::min(std::numeric_limits<unsigned char>::max(), static_cast<unsigned char>(std::max(5, (RevTime - getEnvir()->getTime()) / 1000)));
        Packet *p = new S::ObjectHealth();
        p->ObjectID = ObjectID;
        p->Percent = getPercentHealth();
        p->Expire = time;

        if (getEnvir()->getTime() < RevTime)
        {
            getCurrentMap()->Broadcast(p, getCurrentLocation());

//C# TO C++ CONVERTER TODO TASK: A 'delete p' statement was not added since p was passed to a method or constructor. Handle memory management manually.
            return;
        }

        if (getRace() == ObjectType::Monster && !AutoRev && Master == nullptr)
        {
//C# TO C++ CONVERTER TODO TASK: A 'delete p' statement was not added since p was passed to a method or constructor. Handle memory management manually.
            return;
        }

        if (getRace() == ObjectType::Player)
        {
            if (!GroupMembers.empty()) //Send HP to group
            {
                for (int i = 0; i < GroupMembers.size(); i++)
                {
                    PlayerObject *member = GroupMembers[i];

                    if (this == member)
                    {
                        continue;
                    }
                    if (member->getCurrentMap() != getCurrentMap() || !Functions::InRange(member->getCurrentLocation(), getCurrentLocation(), Globals::DataRange))
                    {
                        continue;
                    }
                    member->Enqueue(p);
                }
            }

//C# TO C++ CONVERTER TODO TASK: A 'delete p' statement was not added since p was passed to a method or constructor. Handle memory management manually.
            return;
        }

        if (Master != nullptr && Master->getRace() == ObjectType::Player)
        {
            PlayerObject *player = static_cast<PlayerObject*>(Master);

            player->Enqueue(p);

            if (!player->GroupMembers.empty()) //Send pet HP to group
            {
                for (int i = 0; i < player->GroupMembers.size(); i++)
                {
                    PlayerObject *member = player->GroupMembers[i];

                    if (player == member)
                    {
                        continue;
                    }

                    if (member->getCurrentMap() != getCurrentMap() || !Functions::InRange(member->getCurrentLocation(), getCurrentLocation(), Globals::DataRange))
                    {
                        continue;
                    }
                    member->Enqueue(p);
                }
            }
        }


        if (EXPOwner != nullptr && EXPOwner->getRace() == ObjectType::Player)
        {
            PlayerObject *player = static_cast<PlayerObject*>(EXPOwner);

            if (player->IsMember(Master))
            {
//C# TO C++ CONVERTER TODO TASK: A 'delete p' statement was not added since p was passed to a method or constructor. Handle memory management manually.
                return;
            }

            player->Enqueue(p);

            if (!player->GroupMembers.empty())
            {
                for (int i = 0; i < player->GroupMembers.size(); i++)
                {
                    PlayerObject *member = player->GroupMembers[i];

                    if (player == member)
                    {
                        continue;
                    }
                    if (member->getCurrentMap() != getCurrentMap() || !Functions::InRange(member->getCurrentLocation(), getCurrentLocation(), Globals::DataRange))
                    {
                        continue;
                    }
                    member->Enqueue(p);
                }
            }
        }

//C# TO C++ CONVERTER TODO TASK: A 'delete p' statement was not added since p was passed to a method or constructor. Handle memory management manually.
    }

    void MapObject::BroadcastDamageIndicator(DamageType type, int damage)
    {
        Packet *p = new S::DamageIndicator();
        p->ObjectID = ObjectID;
        p->Damage = damage;
        p->Type = type;

        if (getRace() == ObjectType::Player)
        {
            PlayerObject *player = static_cast<PlayerObject*>(this);
            player->Enqueue(p);
        }
        Broadcast(p);

//C# TO C++ CONVERTER TODO TASK: A 'delete p' statement was not added since p was passed to a method or constructor. Handle memory management manually.
    }

    bool MapObject::IsMember(MapObject *member)
    {
        if (member == this)
        {
            return true;
        }
        if (GroupMembers.empty() || member == nullptr)
        {
            return false;
        }

        for (int i = 0; i < GroupMembers.size(); i++)
        {
            if (GroupMembers[i] == member)
            {
                return true;
            }
        }

        return false;
    }

    void MapObject::setInTrapRock(bool value)
    {
        if (dynamic_cast<PlayerObject*>(this) != nullptr)
        {
            PlayerObject *player = static_cast<PlayerObject*>(this);
            S::InTrapRock *tempVar = new S::InTrapRock();
            tempVar->Trapped = value;
            player->Enqueue(tempVar);

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was passed to a method or constructor. Handle memory management manually.
        }
    }

    bool MapObject::getInTrapRock() const
    {
        Point *checklocation;

        for (int i = 0; i <= 6; i += 2)
        {
            checklocation = Functions::PointMove(getCurrentLocation(), static_cast<MirDirection>(i), 1);

            if (checklocation->X < 0)
            {
                continue;
            }
            if (checklocation->X >= getCurrentMap()->Width)
            {
                continue;
            }
            if (checklocation->Y < 0)
            {
                continue;
            }
            if (checklocation->Y >= getCurrentMap()->Height)
            {
                continue;
            }

            Cell *cell = getCurrentMap()->GetCell(checklocation->X, checklocation->Y);
            if (!cell->getValid() || cell->Objects.empty())
            {
                continue;
            }

            for (int j = 0; j < cell->Objects.size(); j++)
            {
                MapObject *ob = cell->Objects[j];
                switch (ob->getRace())
                {
                    case ObjectType::Monster:
                        if (dynamic_cast<TrapRock*>(ob) != nullptr)
                        {
                            TrapRock *rock = static_cast<TrapRock*>(ob);
                            if (rock->Dead)
                            {
                                continue;
                            }
                            if (rock->getTarget() != this)
                            {
                                continue;
                            }
                            if (!rock->Visible)
                            {
                                continue;
                            }
                        }
                        else
                        {
                            continue;
                        }

                        return true;
                    default:
                        continue;
                }
            }
        }
        return false;
    }

    Poison::Poison()
    {
    }

    Poison::Poison(BinaryReader *reader)
    {
        Owner = nullptr;
        PType = static_cast<PoisonType>(reader->ReadByte());
        Value = reader->ReadInt32();
        Duration = reader->ReadInt64();
        Time = reader->ReadInt64();
        TickTime = reader->ReadInt64();
        TickSpeed = reader->ReadInt64();
    }

    std::unordered_map<std::string, std::any> Buff::getData() const
    {
        return Data;
    }

    void Buff::setData(const std::unordered_map<std::string, std::any> &value)
    {
        Data = value;
    }

    Buff::Buff()
    {
        Stats = new Stats();
        setData(std::unordered_map<std::string, std::any>());
    }

    Buff::Buff(BinaryReader *reader)
    {
        Type = static_cast<BuffType>(reader->ReadByte());
        Caster = nullptr;
        Visible = reader->ReadBoolean();
        ObjectID = reader->ReadUInt32();
        ExpireTime = reader->ReadInt64();

        if (Envir::LoadVersion <= 84)
        {
            Values = std::vector<int>(reader->ReadInt32());

            for (int i = 0; i < Values.size(); i++)
            {
                Values[i] = reader->ReadInt32();
            }

            Infinite = reader->ReadBoolean();

            Stats = new Stats();
            setData(std::unordered_map<std::string, std::any>());
        }
        else
        {
            Stackable = reader->ReadBoolean();

            Values = std::vector<int>(0);
            Stats = new Stats(reader);
            setData(std::unordered_map<std::string, std::any>());

            int count = reader->ReadInt32();

            for (int i = 0; i < count; i++)
            {
                auto key = reader->ReadString();
                auto length = reader->ReadInt32();

                auto array = std::vector<unsigned char>(length);

                for (int j = 0; j < array.size(); j++)
                {
                    array[j] = reader->ReadByte();
                }

                getData()[key] = Functions::DeserializeFromBytes(array);
            }

            if (Envir::LoadVersion > 86)
            {
                count = reader->ReadInt32();

                Values = std::vector<int>(count);

                for (int i = 0; i < count; i++)
                {
                    Values[i] = reader->ReadInt32();
                }
            }
        }
    }

    void Buff::Save(BinaryWriter *writer)
    {
        writer->Write(static_cast<unsigned char>(Type));
        writer->Write(Visible);
        writer->Write(ObjectID);
        writer->Write(ExpireTime);

        writer->Write(Stackable);

        Stats->Save(writer);

        writer->Write(getData().size());

        for (auto pair : getData())
        {
            auto bytes = Functions::SerializeToBytes(pair.second);

            writer->Write(pair.first);
            writer->Write(bytes.size());

            for (int i = 0; i < bytes.size(); i++)
            {
                writer->Write(bytes[i]);
            }
        }

        writer->Write(Values.size());

        for (int i = 0; i < Values.size(); i++)
        {
            writer->Write(Values[i]);
        }
    }

    void Buff::Set(const std::string &key, std::any val)
    {
        getData()[key] = val;
    }

    ClientBuff *Buff::ToClientBuff()
    {
        ClientBuff *tempVar = new ClientBuff();
        tempVar->Type = Type;
        tempVar->Caster = Caster == nullptr ? "" : ((Caster->getName() != "") ? Caster->getName() : "");
        tempVar->ObjectID = ObjectID;
        tempVar->Visible = Visible;
        tempVar->Infinite = Infinite;
        tempVar->ExpireTime = ExpireTime;
        tempVar->Stats = new Stats(Stats);
        tempVar->Values = Values;

//C# TO C++ CONVERTER TODO TASK: A 'delete tempVar' statement was not added since tempVar was used in a 'return' or 'throw' statement.
        return tempVar;
    }
}
